package com.pan.api.utils;


import com.pan.api.anno.PanComment;
import com.pan.api.entity.ArgumentInformation;
import com.pan.api.entity.ControllerInformation;
import com.pan.api.entity.MethodInformation;
import com.pan.api.entity.ParameterRequestType;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Generate interfaces documentation
 */
@Deprecated
 class ScanApi
    implements
//    ApplicationContextAware,
        PanRecordUtil
{
    private ApplicationContext applicationContext;

    public ScanApi(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * @param clazz
     * @return 拥有 @Controller @Rest
     */
    private Class getControllerClazz(Class clazz){
        for (Class c = clazz;c != Object.class; c = c.getSuperclass()){
            if (clazz.getAnnotations().length ==0) continue;
            if (c.isAnnotationPresent(Controller.class)|| c.isAnnotationPresent(RestController.class))return c;
        }
        return null;
    }
    //argument handle
    private List<ArgumentInformation> argHandle(Method method){
        List<ArgumentInformation> resultList;
        Parameter[] parameters = method.getParameters();
        for (Parameter param: parameters){
            ArgumentInformation argumentInformation = new ArgumentInformation();
            Annotation annotation = null;
            if ((annotation = param.getAnnotation(PanComment.class))==null){
                String value = ((PanComment) annotation).value();
                argumentInformation.setComment(value);
            }
            if ((annotation = param.getAnnotation(PathVariable.class))==null){
                String value = ((PathVariable) annotation).value();
                argumentInformation.setParamType(ParameterRequestType.Path);
                argumentInformation.setParamClass(param.getClass());
                argumentInformation.setParamName(value.length()==0?param.getName():value);
            }
        }
        return null;
    }

    //method handle
    private List<MethodInformation>  apiHandle(Class clazz) {
        List<MethodInformation> resultList = new ArrayList<>();
        Method[] methods = clazz.getMethods();
        for (Method method: methods){
            if (method.getAnnotations().length == 0) continue;
            Annotation annotation;
            RequestMethod[] types;
            String[] path;
            if ((annotation = method.getAnnotation(RequestMapping.class))!= null){
                RequestMapping reqMap = (RequestMapping) annotation;
                path = reqMap.value();
                types = reqMap.method();
                if (types.length == 0){
                    types = RequestMethod.values();
                }
            }else if ((annotation = method.getAnnotation(GetMapping.class))!= null){
                types = new RequestMethod[]{RequestMethod.GET};
                GetMapping reqMap = (GetMapping) annotation;
                path = reqMap.value();
            }else if ((annotation = method.getAnnotation(PostMapping.class))!= null){
                types = new RequestMethod[]{RequestMethod.POST};
                PostMapping reqMap = (PostMapping) annotation;
                path = reqMap.value();
            }else if ((annotation = method.getAnnotation(PutMapping.class))!= null){
                types = new RequestMethod[]{RequestMethod.PUT};
                PutMapping reqMap = (PutMapping) annotation;
                path = reqMap.value();
            }else if ((annotation = method.getAnnotation(DeleteMapping.class))!= null){
                types = new RequestMethod[]{RequestMethod.DELETE};
                DeleteMapping reqMap = (DeleteMapping) annotation;
                path = reqMap.value();
            } else {
                continue;
            }
            MethodInformation methodInformation = new MethodInformation();
            String[] typesStrArr = new String[types.length];
            for (int i = 0; i < types.length; i++) {
                typesStrArr[i] = types[i].name();
            }
            methodInformation.setRequestType(typesStrArr);
            methodInformation.setUrl(path.length == 1?path[0]:Arrays.toString(path));
            if (method.getAnnotation(ResponseBody.class)!=null){
                methodInformation.setJsonXml(true);
            }else {
                methodInformation.setJsonXml(false);
            }
            if ((annotation = method.getAnnotation(PanComment.class))!= null){
                String value = ((PanComment) annotation).value();
                methodInformation.setComment(value);
            }else {
                methodInformation.setComment("");
            }
            methodInformation.setMethodName(method.getName());
            List<ArgumentInformation> argumentInformationList = argHandle(method);
            methodInformation.setArgList(argumentInformationList);
            resultList.add(methodInformation);
        }
        return resultList;
    }
    //Controller Handle
    private ControllerInformation controllerHandle(Class clazz){

        ControllerInformation ci = new ControllerInformation();
        if (clazz.isAnnotationPresent(RestController.class)){
            ci.setJsonXml(true);
        }else if (clazz.isAnnotationPresent(Controller.class)){
            ci.setJsonXml(false);
        }else {
            throw new RuntimeException("This class isn't Controller: "+clazz);
        }
        if (clazz.isAnnotationPresent(RequestMapping.class)){
            RequestMapping requestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
            String[] path = requestMapping.path();
            String pre = "";
            if (path.length > 1){
                pre = Arrays.toString(path);
            }else {
                pre = path[0].length() == 0 ? "":path[0];
            }
            ci.setPrefix(pre);
        }
        if (clazz.isAnnotationPresent(PanComment.class)){
            PanComment panComment = (PanComment) clazz.getAnnotation(PanComment.class);
            ci.setComment(panComment.value());
        }else {
            ci.setComment(null);
        }

        ci.setControllerName(clazz.getName());
        List<MethodInformation> apiList = apiHandle(clazz);
        ci.setMethodList(apiList);
        ci.setMethodList(null);
        return ci;
    }
    public void run(){
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        String suffix = "Controller";

        List<ControllerInformation> resultList = new ArrayList<>();

        pan: for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = applicationContext.getBean(beanDefinitionName);
            Class<?> clazz = bean.getClass();
            if (clazz.getName().endsWith(suffix)){
                Class controllerClazz = getControllerClazz(clazz);
                if (controllerClazz == null) continue pan;
                ControllerInformation controllerInformation = controllerHandle(clazz);
                resultList.add(controllerInformation);
            }
        }
    }


    @FunctionalInterface
    public interface BeanMap {
        Object map(String name);
    }
}
